package com.xiaoyang.lotterysystem.service.impl;

import com.xiaoyang.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.xiaoyang.lotterysystem.common.utils.JacksonUtil;
import com.xiaoyang.lotterysystem.common.utils.RedisUtil;
import com.xiaoyang.lotterysystem.controller.param.DrawPrizeParam;
import com.xiaoyang.lotterysystem.controller.param.WinningRecordParam;
import com.xiaoyang.lotterysystem.dao.dataobject.*;
import com.xiaoyang.lotterysystem.dao.mapper.*;
import com.xiaoyang.lotterysystem.service.DrawPrizeService;
import com.xiaoyang.lotterysystem.service.dto.WinningRecordDTO;
import com.xiaoyang.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.xiaoyang.lotterysystem.service.enums.ActivityStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.xiaoyang.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.xiaoyang.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Slf4j
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private static final String WINNING_RECORD_PREFIX = "WINNING_RECORD_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        // 消息体
        Map<String, String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        // 发消息: 交换机 绑定的key 消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        log.info("MQ消息发送成功:map={}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {

        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        // 奖品是否存在 从 activity_prize 表获取,原因是保存activity 做了本地事务,保持了一致性
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectAPId(param.getActivityId(), param.getPrizeId());

        // 活动是否存在
        if (null ==  activityDO || null == activityPrizeDO) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            log.info("校验抽奖请求失败!, 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
        }

        // 活动是否有效
        if (activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            log.info("校验抽奖请求失败!, 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;
        }

        // 奖品是否有效
        if (activityPrizeDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            log.info("校验抽奖请求失败!, 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMsg());
            return false;
        }

        // 中奖者人数是否和设置奖品数量一致
        if (activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
//            throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            log.info("校验抽奖请求失败!, 失败原因:{}",
                    ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR.getMsg());
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {

        // 查询中奖信息:人员, 活动, 奖品, 活动关联奖品表...
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList = userMapper.batchSelectByIds(param.getWinnerList()
                                                .stream()
                                                .map(DrawPrizeParam.winner::getUserId)
                                                .collect(Collectors.toList())
        );
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectAPId(param.getActivityId(), param.getPrizeId());

        // 构造中奖者记录, 保存
        // 整合用户中奖记录
        List<WinningRecordDO> winningRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());

                    return winningRecordDO;
                }).collect(Collectors.toList()
        );
        // 将中奖者记录插入数据表中
        winningRecordMapper.batchInsert(winningRecordDOList);

        // 缓存中奖者记录
        // 1. 缓存奖品维度中奖记录
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(),
                winningRecordDOList,
                WINNING_RECORDS_TIMEOUT);

        // 2. 缓存活动维度中奖记录
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if (null == activityId) {
            log.warn("要删除的中奖记录相关的活动id为空!");
            return;
        }
        // 删除数据表
        winningRecordMapper.deleteRecords(activityId, prizeId);

        // 删除缓存(奖品维度, 活动维度)
        if (null != prizeId) {
            // 删除奖品维度的缓存
            deleteWinningRecords(activityId + "_" + prizeId);
        }
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordDTO> getRecords(WinningRecordParam param) {
        // 查询redis: 奖品 活动
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWinningRecordDTOList(winningRecordDOList);
        }

        // redis不存在 查表
        winningRecordDOList = winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(), param.getPrizeId());

        // 存放redis
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            log.info("查询的中奖记录为空! {}", JacksonUtil.writeValueAsString(winningRecordDOList));
            return Arrays.asList();
        }
        cacheWinningRecords(key, winningRecordDOList, WINNING_RECORDS_TIMEOUT);
        // 返回
        return convertToWinningRecordDTOList(winningRecordDOList);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinningRecordDO> winningRecordDOList) {
       if (CollectionUtils.isEmpty(winningRecordDOList)) {
           return Arrays.asList();
       }
       return winningRecordDOList.stream()
               .map(winningRecordDO -> {
                   WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                   winningRecordDTO.setWinnerId(winningRecordDO.getWinnerId());
                   winningRecordDTO.setWinnerName(winningRecordDO.getWinnerName());
                   winningRecordDTO.setPrizeName(winningRecordDO.getPrizeName());
                   winningRecordDTO.setPrizeTier(
                           ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                   winningRecordDTO.setWinningTime(winningRecordDO.getWinningTime());

                   return winningRecordDTO;
               }).collect(Collectors.toList());
    }

    /**
     * 从缓存中删除中奖记录
     */
    private void deleteWinningRecords(String key) {
        try {
            if (redisUtil.hasKey(WINNING_RECORD_PREFIX + key)) {
                // 存在就删除
                redisUtil.del(WINNING_RECORD_PREFIX + key);
            }
        } catch (Exception e) {
            log.error("删除中奖记录缓存异常, key:{}" , key);
        }
    }

    /**
     * 缓存中奖者记录
     * @param key
     * @param winningRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winningRecordDOList,
                                     Long time) {
        String str = "";
        try {
            if (!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winningRecordDOList)) {
                log.warn("要缓存的内容为空! key:{}, value:{}",
                        key, JacksonUtil.writeValueAsString(winningRecordDOList));
                return;
            }

            str = JacksonUtil.writeValueAsString(winningRecordDOList); // 序列化
            redisUtil.set(WINNING_RECORD_PREFIX + key, str, time);
        } catch (Exception e) {
            log.error("缓存中奖记录异常! key:{}, value:{}", WINNING_RECORD_PREFIX + key, str);
        }
    }

    /**
     * 从缓存中获取记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key) {

        try {
            if (!StringUtils.hasText(key)) {
                log.warn("要从缓存查询的中奖记录的key为空!");
                return Arrays.asList();
            }

            String str = redisUtil.get(WINNING_RECORD_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }
            List<WinningRecordDO> winningRecordDOList = JacksonUtil.readListValue(str, WinningRecordDO.class);
            return winningRecordDOList;
        } catch (Exception e) {
            log.error("从缓存程序中奖记录异常! key:{}", WINNING_RECORD_PREFIX + key);
            return Arrays.asList();
        }
    }
}
